package com.aliang.shopping.controller;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.aliang.shopping.common.PageResult;
import com.aliang.shopping.common.Result;
import com.aliang.shopping.common.ResultCodeEnum;
import com.aliang.shopping.contact.ResultMsgContact;
import com.aliang.shopping.exception.DataException;
import com.aliang.shopping.exception.ServiceException;
import com.aliang.shopping.log.annotation.OperateLog;
import com.aliang.shopping.log.enums.OperateType;
import com.aliang.shopping.model.enums.*;
import com.aliang.shopping.model.po.*;
import com.aliang.shopping.model.vo.cart.CartShowVO;
import com.aliang.shopping.model.vo.orders.OrdersFrontVO;
import com.aliang.shopping.model.vo.orders.OrdersVO;
import com.aliang.shopping.security.Auth;
import com.aliang.shopping.security.PreventRepeatSubmit;
import com.aliang.shopping.security.auth.UserHolder;
import com.aliang.shopping.service.*;
import com.aliang.shopping.util.DataUtil;
import com.aliang.shopping.util.InputDataUtil;
import com.aliang.shopping.util.RedisDelayUtil;
import com.aliang.shopping.util.ali.PayTemplate;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.AllArgsConstructor;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/orders")
@AllArgsConstructor
@Api
public class OrdersController {

    private final OrdersInfoService ordersInfoService;
    private final OrdersItemService ordersItemService;
    private final CartService cartService;
    private final RedisDelayUtil redisDelayUtil;


    @PostMapping("/createOrders")
    @ApiOperation("创建订单")
    @PreventRepeatSubmit
    @OperateLog(title = "创建订单", type = OperateType.INSERT)
    public Result<Object> createOrders(@RequestBody List<Long> cartIds) {
        //获取登录用户Id
        Long userId = UserHolder.getUserId();
        if (cartIds.isEmpty()) {
            throw new DataException(ResultCodeEnum.DATA_LACK);
        }
        //获取购物车列表
        List<CartShowVO> cartVOList = this.cartService.getCartShowVOByIds(cartIds, userId);
        if (cartVOList.isEmpty()) {
            throw new DataException(ResultCodeEnum.DATA_LACK);
        }
        // 生成支付编号
        Long payNo = IdUtil.getSnowflakeNextId();
        // 存储临时订单存储对象
        List<OrdersInfo> ordersInfoList = new ArrayList<>();
        cartVOList.forEach((item) -> {
            //创建订单
            OrdersInfo ordersInfo = new OrdersInfo();
            ordersInfo.setUserId(userId); //用户Id
            ordersInfo.setPayNo(payNo); // 支付编号
            ordersInfo.setBusinessId(item.getId()); // 商家Id
            ordersInfo.setPayType(PayTypeEnum.ALIPAY.getCode()); //支付方式
            ordersInfo.setStatus(OrdersStatusEnum.WAIT_PAYMENT.getCode()); // 订单状态待支付
            //统计支付价格
            BigDecimal total = item.getCartInfo().stream().map(
                    (cart) -> cart.getGoodsPrice().multiply(BigDecimal.valueOf(cart.getGoodsNum()))
            ).reduce(BigDecimal.ZERO, BigDecimal::add);
            ordersInfo.setPayPrice(total); //设置订单价格
            // 存入临时对象
            ordersInfoList.add(ordersInfo);
        });
        // 存入数据库
        boolean save = ordersInfoService.saveBatch(ordersInfoList);
        if (!save) {
            return Result.fail().message(ResultMsgContact.ORDERS_ADD_FAIL);
        }
        //将订单信息对象id和商家id转换成map
        Map<Long, Long> orderNoMap = ordersInfoList.stream().collect(Collectors.toMap(
                OrdersInfo::getBusinessId,
                OrdersInfo::getId
        ));
        // 存储临时订单列表对象
        List<OrdersItem> ordersItemList = new ArrayList<>();
        cartVOList.forEach((item) -> {
            //获取订单id
            Long ordersNo = orderNoMap.get(item.getId());
            // 读取订单详情数据
            List<OrdersItem> ordersItems = item.getCartInfo().stream().map((cart) -> {
                OrdersItem ordersItem = new OrdersItem();
                ordersItem.setOrdersNo(ordersNo);//设置订单编号
                ordersItem.setGoodsSkuId(cart.getGoodsId()); //设置商品SKU编号
                ordersItem.setGoodsNum(cart.getGoodsNum()); //设置商品数量
                ordersItem.setGoodsPrice(cart.getGoodsPrice()); // 设置商品价格
                ordersItem.setGoodsScope(cart.getGoodsScope()); // 设置商品折扣
                return ordersItem;
            }).collect(Collectors.toList());
            //存储在临时对象中
            ordersItemList.addAll(ordersItems);
        });
        //批量新增
        boolean saveBatch = ordersItemService.saveBatch(ordersItemList);
        //删除购物车信息
        this.cartService.removeByIds(cartIds);
        // 将支付编号加入延迟队列，一天后未支付全部取消
        redisDelayUtil.offer(payNo.toString());
        //返回支付编号
        return saveBatch ? Result.ok(payNo) : Result.fail()
                .message(ResultMsgContact.ORDERS_ADD_FAIL);
    }

    private final GoodsSkuService goodsSkuService;
    private final BusinessService businessService;

    @PostMapping("/newOrderPay/{businessId}/{goodsSkuId}/{goodsNum}")
    @ApiOperation("立即下单")
    @PreventRepeatSubmit
    @OperateLog(title = "立即下单", type = OperateType.INSERT)
    public Result<Object> newOrderPay(@PathVariable
                                      @ApiParam(value = "商家Id", required = true)
                                      Long businessId, @PathVariable
                                      @ApiParam(value = "商品SKU编号", required = true)
                                      Long goodsSkuId,
                                      @PathVariable
                                      @ApiParam(value = "商品数量", required = true)
                                      Integer goodsNum
    ) {
        //获取登录用户Id
        Long userId = UserHolder.getUserId();
        //判断该商家是否存在
        int countBusiness = this.businessService.count(
                new LambdaQueryWrapper<Business>()
                        .eq(Business::getId, businessId)
        );
        if (countBusiness == 0) {
            // 商家不存在
            throw new DataException(ResultCodeEnum.BUSINESS_NOT_EXIST);
        }
        //根据skuId获取商品信息
        GoodsSku byId = this.goodsSkuService
                .getOne(new LambdaQueryWrapper<GoodsSku>()
                        // 根据Id查询
                        .eq(GoodsSku::getId, goodsSkuId)
                        //库存大于等于购买数量
                        .ge(GoodsSku::getStore, goodsNum)
                        .or()
                        .eq(GoodsSku::getId, goodsSkuId)
                        // 或者预存库存大于购买数量的
                        .ge(GoodsSku::getReservedStore, goodsNum)
                );
        // 数据为空，库存不足抛出异常
        if (ObjectUtil.isNull(byId)) {
            // 库存不足，抛出错误
            throw new DataException(ResultCodeEnum.GOODS_STOCK_NOT_ENOUGH);
        }
        // 如果库存小于购买数量，删除购物车最远的订购数据
        if (byId.getStore() < goodsNum) {
            // 查询购物车一周外的订购数据
            // 获取近七天的开始时间
            LocalDateTime weekStartTime = DataUtil.getWeekStartTime();
            List<Cart> cartList = this.cartService.list(
                    new LambdaQueryWrapper<Cart>()
                            //获取购物车Id、商品数、用户Id
                            .select(Cart::getId, Cart::getGoodsNum, Cart::getUserId)
                            // 小于近七天的开始时间
                            .ge(Cart::getCreateTime, weekStartTime)
                            // 创建时间升序，获取最远的数据
                            .orderByAsc(Cart::getCreateTime)
                            // 根据商品数获取数据条数，每个购物车最少有一个商品数
                            .last("limit " + goodsNum)
            );
            // 存储要删除的用户id
            List<Long> delUserId = new ArrayList<>();
            // 存储要数的购物车id
            List<Long> delCartId = new ArrayList<>();
            //统计的数量
            Integer count = 0;
            for (Cart cart : cartList) {
                count += cart.getGoodsNum();
                delUserId.add(cart.getUserId());
                delCartId.add(cart.getId());
                //如果数量大于购买数量，跳出循环
                if (count >= goodsNum)
                    break;
            }
            // 删除购物车数据
            this.cartService.removeByIds(delCartId);
            // 是否邮件通知删除 todo
            //更新库存
            boolean update = this.goodsSkuService.update(
                    new LambdaUpdateWrapper<GoodsSku>()
                            //更新库存,统计的数量减去购买的数量
                            .set(GoodsSku::getStore, byId.getStore() + (count - goodsNum))
                            //更新预存库存，减去统计的数量
                            .set(GoodsSku::getReservedStore, byId.getReservedStore() - count)
                            //根据skuId查询
                            .eq(GoodsSku::getId, goodsSkuId)
            );
            if (!update) {
                return Result.fail().message(ResultMsgContact.ORDERS_ADD_FAIL);
            }
        }
        //统计订单价格
        BigDecimal total = byId.getPrice()
                .multiply(byId.getScope()) // 乘以折扣
                .multiply(BigDecimal.valueOf(goodsNum)) // 乘以数量
                .setScale(2, RoundingMode.HALF_UP) //设置2位小数
                ;
        //创建订单
        OrdersInfo ordersInfo = new OrdersInfo();
        ordersInfo.setUserId(userId); //用户Id
        Long payNo = IdUtil.getSnowflakeNextId();
        ordersInfo.setPayNo(payNo); //支付编号
        ordersInfo.setBusinessId(businessId); //商家Id
        ordersInfo.setPayType(PayTypeEnum.ALIPAY.getCode()); //支付方式
        ordersInfo.setStatus(OrdersStatusEnum.WAIT_PAYMENT.getCode()); // 订单状态待支付
        ordersInfo.setPayPrice(total); // 支付价格
        // 存入数据库
        boolean save = ordersInfoService.save(ordersInfo);
        if (!save) {
            return Result.fail().message(ResultMsgContact.ORDERS_ADD_FAIL);
        }
        // 创建订单详情
        OrdersItem ordersItem = new OrdersItem();
        ordersItem.setOrdersNo(ordersInfo.getId());//设置订单编号
        ordersItem.setGoodsSkuId(goodsSkuId); //设置商品SKU编号
        ordersItem.setGoodsNum(goodsNum); //设置商品数量
        ordersItem.setGoodsPrice(byId.getPrice()); // 设置商品价格
        ordersItem.setGoodsScope(byId.getScope()); // 设置商品折扣
        //批量新增
        boolean saveBatch = ordersItemService.save(ordersItem);
        // 将支付编号加入延迟队列，一天后未支付全部取消
        redisDelayUtil.offer(payNo.toString());
        // todo 更新库存
        //返回支付编号
        return saveBatch ? Result.ok(payNo) : Result.fail()
                .message(ResultMsgContact.ORDERS_ADD_FAIL);
    }

    private final PayTemplate payTemplate;

    @PutMapping("/pay/{payNo}/{addressId}")
    @ApiOperation("支付订单")
    @OperateLog(title = "支付订单", type = OperateType.UPDATE)
    @Transactional(rollbackFor = Exception.class)
    public Result<String> getOrdersInfo(@PathVariable Long payNo, @PathVariable Long addressId,
                                        @RequestBody String subject) {
        //获取订单信息
        List<OrdersInfo> list = this.ordersInfoService.list(
                new LambdaQueryWrapper<OrdersInfo>()
                        .select(OrdersInfo::getId, OrdersInfo::getPayPrice)
                        .eq(OrdersInfo::getPayNo, payNo)
        );
        if (list.isEmpty()) {
            throw new DataException(ResultCodeEnum.ORDERS_NOT_EXIT);
        }
        // 统计金额
        BigDecimal total = list.stream().map(OrdersInfo::getPayPrice).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_UP);
        //修改订单信息地址信息
        List<OrdersInfo> collect = list.stream().map((ordersInfo -> {
            OrdersInfo update = new OrdersInfo();
            update.setId(ordersInfo.getId());
            update.setAddressId(addressId);
            return update;
        })).collect(Collectors.toList());
        // 修改信息
        this.ordersInfoService.updateBatchById(collect);
        // 发起支付,获取支付表单
        String pay = payTemplate.pay(payNo.toString(), total, subject);
        return Result.ok(pay);
    }

    @PutMapping("/pay/{payNo}")
    @ApiOperation("通过支付编号支付订单,二次支付")
    @OperateLog(title = "通过支付编号支付订单,二次支付", type = OperateType.UPDATE)
    @Transactional(rollbackFor = Exception.class)
    @Auth(role = UserTypeEnum.USER)
    public Result<String> getOrdersInfoByPayNo(@PathVariable Long payNo, @RequestBody String subject) {
        //通过支付编号获取订单信息
        List<OrdersInfo> list = this.ordersInfoService.list(new LambdaQueryWrapper<OrdersInfo>()
                .select(OrdersInfo::getId, OrdersInfo::getPayPrice)
                .eq(OrdersInfo::getPayNo, payNo)
        );
        if (list.isEmpty()) {
            throw new DataException(ResultCodeEnum.ORDERS_NOT_EXIT);
        }
        // 统计金额
        BigDecimal total = list.stream().map(OrdersInfo::getPayPrice).reduce(BigDecimal.ZERO, BigDecimal::add).setScale(2, RoundingMode.HALF_UP);
        // 发起支付,获取支付表单
        String pay = payTemplate.pay(payNo.toString(), total, subject);
        return Result.ok(pay);
    }


    @GetMapping("/getOrdersDetailByPayNo/{payNo}")
    @ApiOperation("支付页获取订单详情信息")
    public Result<List<CartShowVO>> getOrdersDetailByPayNo(@PathVariable Long payNo) {
        //获取订单详情
        List<CartShowVO> ordersItemInfo = this.ordersItemService.getOrdersItemInfoByPayNo(payNo);
        return Result.ok(ordersItemInfo);
    }

    @GetMapping("/getOrdersByUse")
    @ApiOperation("获取当前登录用户的订单信息")
    public Result<List<OrdersFrontVO>> getOrdersByUse() {
        //获取当前登录用户Id
        Long userId = UserHolder.getUserId();
        if (Objects.isNull(userId)) {
            throw new ServiceException(ResultCodeEnum.USER_NOT_LOGIN);
        }
        //获取订单详情
        List<OrdersFrontVO> ordersItemInfo = this.ordersItemService.getOrdersByUseId(userId);
        return Result.ok(ordersItemInfo);
    }

    @GetMapping("/cancelOrders/{orderId}")
    @ApiOperation("根据订单id取消订单")
    public Result<Boolean> cancelOrders(@PathVariable Long orderId) {
        //获取当前登录用户Id
        Long userId = UserHolder.getUserId();
        // 取消订单
        return Result.ok(this.ordersInfoService.cancelOrdersByIdWithUserId(orderId, userId));
    }

    @GetMapping("/getOrdersPageByBusinessId/{pageNum}/{pageSize}")
    @ApiOperation("获取商家的订单分页信息")
    public Result<PageResult<OrdersVO>> getOrdersPageByBusinessId(@PathVariable("pageNum") Integer pageNum, @PathVariable("pageSize") Integer pageSize, Long ordersId, Integer status) {
        //获取当前登录用户Id
        Long businessId = UserHolder.getUserId();
        if (Objects.isNull(businessId)) {
            throw new ServiceException(ResultCodeEnum.USER_NOT_LOGIN);
        }
        //获取订单详情
        PageResult<OrdersVO> result = this.ordersInfoService.getOrdersByBusinessId(businessId, pageNum, pageSize, ordersId, status);
        return Result.ok(result);
    }

    @PutMapping("/toDeliverGoods/{ordersId}")
    @ApiOperation("商家发货")
    @Auth(role = UserTypeEnum.BUSINESS)
    public Result<String> toDeliverGoods(@PathVariable("ordersId") Long ordersId) {
        //获取当前登录用户信息Id
        Long userId = UserHolder.getUserId();
        OrdersInfo byId = this.ordersInfoService.getById(ordersId);
        if (Objects.isNull(byId)
                // 判断是否属于当前商家
                || !Objects.equals(byId.getBusinessId(), userId)
        ) {
            throw new ServiceException(ResultCodeEnum.ORDERS_NOT_EXIT);
        }
        // 判断当前是否是待发货
        if (!Objects.equals(byId.getStatus(), OrdersStatusEnum.WAIT_DELIVERY.getCode())) {
            return Result.fail("").message(ResultMsgContact.ORDERS_STATUS_ERROR);
        }
        // 修改订单状态
        OrdersInfo ordersInfo = new OrdersInfo();
        ordersInfo.setId(ordersId);
        ordersInfo.setStatus(OrdersStatusEnum.WAIT_RECEIVE.getCode());
        // 保存数据
        this.ordersInfoService.updateById(ordersInfo);
        return Result.ok(ResultMsgContact.UPDATE_SUCCESS);
    }

    private final CheckInfoListService checkInfoListService;

    @PutMapping("/submitCheckInfo/{ordersId}/{dataId}/{type}")
    @ApiOperation("用户提交审核信息")
    @Auth(role = UserTypeEnum.USER)
    public Result<String> submitCheckInfo(@PathVariable Long ordersId,
                                          @PathVariable Long dataId,
                                          @PathVariable Integer type,
                                          @RequestBody String userDes) {
        // 判断传入数字是否合法
        List<Integer> collect = Arrays.stream(CheckInfoTypeEnum.values()).map(CheckInfoTypeEnum::getCode).collect(Collectors.toList());
        if (!InputDataUtil.numIsOK(type, collect.get(0), collect.get(1))) {
            throw new ServiceException(ResultCodeEnum.TYPE_NUM_ERR);
        }
        // 获取订单信息
        OrdersInfo ordersInfo = this.ordersInfoService.getById(ordersId);
        if (Objects.isNull(ordersInfo)) {
            throw new ServiceException(ResultCodeEnum.ORDERS_NOT_EXIT);
        }
        // 获取用户id
        Long userId = UserHolder.getUserId();
        if (!Objects.equals(ordersInfo.getUserId(), userId)) {
            throw new ServiceException(ResultCodeEnum.USER_NOT_LOGIN);
        }
        // 封装公共字段
        CheckInfoList checkInfoList = new CheckInfoList();
        checkInfoList.setOrdersId(ordersId);
        checkInfoList.setUserId(userId);
        checkInfoList.setStatus(CheckInfoStatusEnum.SHZ.getCode()); // 审核状态
        checkInfoList.setUserDes(userDes); // 用户描述
        checkInfoList.setDataId(dataId); // 数据Id
        // 修改地址
        if (Objects.equals(type, CheckInfoTypeEnum.ADDRESS.getCode())) {
            // 发货前才可以修改地址
            if (ordersInfo.getStatus() > OrdersStatusEnum.WAIT_DELIVERY.getCode()) {
                throw new ServiceException(ResultCodeEnum.ORDERS_UPDATE_BAD);
            }
            checkInfoList.setType(CheckInfoTypeEnum.ADDRESS.getCode());
        } else {
            checkInfoList.setType(CheckInfoTypeEnum.OUT_PRICE.getCode());
        }
        // 更新数据
        checkInfoListService.saveOrUpdate(checkInfoList);
        return Result.ok(ResultMsgContact.ORDERS_Check_SUC);
    }


    private final UserInfoService userInfoService;
    @PutMapping("/checkInfoOk/{ordersId}/{dataId}/{type}")
    @ApiOperation("商家审核申请信息通过")
    @Auth(role = UserTypeEnum.BUSINESS)
    public Result<String> checkInfoOk(@RequestBody CheckInfoList checkInfoList) {
        // 判断类型传入数字是否合法
        List<Integer> collect = Arrays.stream(CheckInfoTypeEnum.values()).map(CheckInfoTypeEnum::getCode).collect(Collectors.toList());
        if (!InputDataUtil.numIsOK(checkInfoList.getType(), collect.get(0), collect.get(1))) {
            throw new ServiceException(ResultCodeEnum.TYPE_NUM_ERR);
        }
        // 获取审核信息
        CheckInfoList byId = checkInfoListService.getOne(new LambdaQueryWrapper<CheckInfoList>()
                .eq(CheckInfoList::getOrdersId, checkInfoList.getOrdersId())
                .eq(CheckInfoList::getDataId, checkInfoList.getDataId())
                .eq(CheckInfoList::getType, checkInfoList.getType())
                .eq(CheckInfoList::getStatus, CheckInfoStatusEnum.SHZ.getCode())
        );
        // 数据为空抛出异常
        if (Objects.isNull(byId)) {
            throw new ServiceException(ResultCodeEnum.CHECK_INFO_NOT_EXIT);
        }
        // 更新封装数据
        boolean update = this.checkInfoListService.update(new LambdaUpdateWrapper<CheckInfoList>()
                .set(CheckInfoList::getStatus, checkInfoList.getStatus())
                .set(StrUtil.isNotBlank(checkInfoList.getBusinessDes()), CheckInfoList::getBusinessDes, checkInfoList.getBusinessDes()
                )
                .eq(CheckInfoList::getOrdersId, checkInfoList.getOrdersId())
                .eq(CheckInfoList::getDataId, checkInfoList.getDataId())
                .eq(CheckInfoList::getType, checkInfoList.getType())
        );
        // 发送邮箱
        this.userInfoService.sendNoticeMail(Collections.singletonList(byId.getUserId()),
                "订单编号的为" + byId.getOrdersId()+"的订单申请信息审核通过");
        return update ? Result.ok(ResultMsgContact.UPDATE_SUCCESS) : Result.fail(ResultMsgContact.UPDATE_FAIL_SUCCESS);
    }
}
